#include "stddef.h"
#include "stm32f4xx.h"
#include "time_delay.h"

static volatile uint16_t ticks;
static time_syscall_t callback;

static void time_delay_base_init(void)
{
    RCC_ClocksTypeDef RCC_ClockStructure;
    RCC_GetClocksFreq(&RCC_ClockStructure);
    uint32_t apb1_time_freq_mhz = RCC_ClockStructure.PCLK1_Frequency / 1000000ul * 2;

    TIM_TimeBaseInitTypeDef TIM_InitStructure;
    TIM_TimeBaseStructInit(&TIM_InitStructure);
    TIM_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_InitStructure.TIM_Period = 1000 - 1;
    TIM_InitStructure.TIM_Prescaler = apb1_time_freq_mhz - 1;
    TIM_InitStructure.TIM_RepetitionCounter = 0;
    TIM_TimeBaseInit(TIM6, &TIM_InitStructure);

    TIM_ITConfig(TIM6, TIM_IT_Update, ENABLE);
    TIM_Cmd(TIM6, ENABLE);
}

static void time_delay_irq_init(void)
{
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = TIM6_DAC_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_Init(&NVIC_InitStructure);
}

void time_delay_init(void)
{
    time_delay_base_init();
    time_delay_irq_init();
}

uint64_t time_now(void)
{
    uint64_t timer1, timer2, cnt;
    do 
    {
        timer1 = ticks;
        cnt = TIM_GetCounter(TIM6);
        timer2 = ticks;
    } while (timer1 != timer2);

    return timer1 + cnt;
}

uint64_t time_get_us(void)
{
    return time_now();
}

uint64_t time_get_ms(void)
{
    return time_now() / 1000;
}

void time_delay_us(uint32_t us)
{
    uint64_t start = time_now();
    while (time_now() - start < (uint64_t)us);
}

void time_delay_ms(uint32_t ms)
{
    uint64_t start = time_now();
    while (time_now() - start < (uint64_t)ms * 1000);
}

void time_syscall_register(time_syscall_t cb)
{
    callback = cb;
}

// 触发周期1ms, tick数单位us
void TIM6_DAC_IRQHandler(void)
{
    if (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET)
    {
        TIM_ClearITPendingBit(TIM6, TIM_IT_Update);
        ticks += 1000;
        if (callback != NULL)
        {
            callback();
        }
    }
}
